home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / Python 1.3.3 / Python 133 SRC / Extensions / img / imgsgimodule.c < prev    next >
Text File  |  1996-01-15  |  25KB  |  1,065 lines

  1. /***********************************************************
  2. Copyright 1991, 1992, 1993, 1994 by Stichting Mathematisch Centrum,
  3. Amsterdam, The Netherlands.
  4.  
  5.                         All Rights Reserved
  6.  
  7. Permission to use, copy, modify, and distribute this software and its 
  8. documentation for any purpose and without fee is hereby granted, 
  9. provided that the above copyright notice appear in all copies and that
  10. both that copyright notice and this permission notice appear in 
  11. supporting documentation, and that the names of Stichting Mathematisch
  12. Centrum or CWI not be used in advertising or publicity pertaining to
  13. distribution of the software without specific, written prior permission.
  14.  
  15. STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
  16. THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  17. FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
  18. FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  19. WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  20. ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
  21. OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  22.  
  23. ******************************************************************/
  24.  
  25. /* rgb objects */
  26.  
  27. #include "Python.h"
  28. #include "import.h"
  29.  
  30. static PyObject *format_rgb, *format_rgb_b2t, *format_rgb_choices;
  31. static PyObject *format_grey, *format_grey_b2t, *format_xgrey,
  32.         *format_xgrey_b2t, *format_grey_choices;
  33. static PyObject *format_rgb8, *format_rgb8_b2t, *format_xrgb8,
  34.         *format_xrgb8_b2t, *format_rgb8_choices;
  35. static PyObject *format_choices;
  36.  
  37. #define is_4bytefmt(fmt)    (fmt==format_rgb||fmt==format_rgb_b2t)
  38. #define is_alignfmt(fmt)    (fmt==format_grey||fmt==format_grey_b2t||\
  39.                  fmt==format_rgb8||fmt==format_rgb8_b2t)
  40. #define is_b2tfmt(fmt)        (fmt==format_grey_b2t||fmt==format_xgrey_b2t||\
  41.                  fmt==format_rgb8_b2t||fmt==format_xrgb8_b2t||\
  42.                  fmt==format_rgb_b2t)
  43. #define is_ditherfmt(fmt)    (fmt==format_rgb8||fmt==format_rgb8_b2t||\
  44.                  fmt==format_xrgb8||fmt==format_xrgb8_b2t)
  45. #define is_greyfmt(fmt)        (fmt==format_grey||fmt==format_grey_b2t||\
  46.                  fmt==format_xgrey||fmt==format_xgrey_b2t)
  47.  
  48. typedef struct {
  49.     PyObject_HEAD
  50.     PyObject    *dict;        /* Attributes dictionary */
  51.     int    is_reader;    /* TRUE if this is a reader */
  52.     char    *filename;    /* filename of the image file */
  53.     FILE    *filep;
  54.     int    width, height;
  55.     int    nchannels;
  56.     int    rle;
  57.     int    colormapID;
  58. } rgbobject;
  59.  
  60. static PyObject *errobject;
  61.  
  62. staticforward PyTypeObject Rgbtype;
  63.  
  64. #define is_rgbobject(v)        ((v)->ob_type == &Rgbtype)
  65.  
  66. static char doc_rgb[] =
  67.     "This object can be used to read/write RGB image files.\n"
  68.     "The 'width', 'height' and 'format' attributes give info about the\n"
  69.     "image data read (or to be written)";
  70.  
  71. /* Routine to easily obtain C data from the dict python data */
  72. int
  73. rgbselfattr(self, name, fmt, ptr, wanterr)
  74.     rgbobject *self;
  75.     char *name;
  76.     char *fmt;
  77.     void *ptr;
  78.     int wanterr;
  79. {
  80.     PyObject *obj;
  81.     char errbuf[100];
  82.  
  83.     obj = PyDict_GetItemString(self->dict, name);
  84.     if (obj == NULL) {
  85.         if (wanterr) {
  86.             sprintf(errbuf, "Required attribute '%s' not set", name);
  87.             PyErr_SetString(errobject, errbuf);
  88.             return 0;
  89.         } else {
  90.             PyErr_Clear();
  91.             return 0;
  92.         }
  93.     }
  94.     if (!PyArg_Parse(obj, fmt, ptr)) {
  95.         if (!wanterr)
  96.             PyErr_Clear();
  97.         return 0;
  98.     }
  99.     return 1;
  100. }
  101.  
  102. /* Routine to easily insert integer into dictionary */
  103. rgbsetintattr(self, name, value)
  104.     rgbobject *self;
  105.     char *name;
  106.     int value;
  107. {
  108.     PyObject *obj;
  109.     int rv;
  110.  
  111.     obj = PyInt_FromLong(value);
  112.     rv = PyDict_SetItemString(self->dict, name, obj);
  113.     Py_DECREF(obj);
  114.     return rv;
  115. }
  116.  
  117. static rgbobject *
  118. newrgbobject()
  119. {
  120.     rgbobject *xp;
  121.     xp = PyObject_NEW(rgbobject, &Rgbtype);
  122.     if (xp == NULL)
  123.         return NULL;
  124.     xp->dict = PyDict_New();
  125.     xp->filename = NULL;
  126.     xp->filep = NULL;
  127.     return xp;
  128. }
  129.  
  130. static int
  131. RGBreadheader(self)
  132.     rgbobject *self;
  133. {
  134.     unsigned char header[512];
  135.     int dimension;
  136.  
  137.     /*
  138.        The RGB header looks as follows:
  139.        2 bytes    IRIS image file magic number
  140.        1 byte    storage format
  141.        1 byte    number of bytes per pixel channel
  142.        2 bytes    number of dimensions
  143.        2 bytes    x size in pixels
  144.        2 bytes    y size in pixels
  145.        2 bytes    number of channels
  146.        4 bytes    minimum pixel value
  147.        4 bytes    maximum pixel value
  148.        4 bytes    ignored
  149.        80 bytes    image name
  150.        4 bytes    colormap ID
  151.        404 bytes    ignored
  152.      */
  153.     if (fread(header, 1, 512, self->filep) != 512) {
  154.         PyErr_SetString(errobject, "Truncated RGB file");
  155.         return 0;
  156.     }
  157.     if ((header[0]<<8|header[1]) != 474) {
  158.         PyErr_SetString(errobject, "Not an RGB file");
  159.         return 0;
  160.     }
  161.     self->rle = header[2];
  162.     if (self->rle != 0 && self->rle != 1)
  163.         goto BadRGB;
  164.     if (header[3] != 1)    /* only support 1 byte per pixel */
  165.         goto BadRGB;
  166.     dimension = header[4]<<8|header[5];
  167.     if (dimension < 1 || dimension > 3)
  168.         goto BadRGB;
  169.     self->width = header[6]<<8|header[7];
  170.     self->height = header[8]<<8|header[9];
  171.     rgbsetintattr(self, "width", self->width);
  172.     rgbsetintattr(self, "height", self->height);
  173.     self->nchannels = header[10]<<8|header[11];
  174.     if (self->nchannels != 1 && self->nchannels != 3 && self->nchannels != 4)
  175.         goto BadRGB;
  176.     if (dimension == 1) {
  177.         if (self->height != 1 || self->nchannels != 1)
  178.             goto BadRGB;
  179.     } else if (dimension == 2) {
  180.         if (self->nchannels != 1)
  181.             goto BadRGB;
  182.     }
  183.     /*
  184.     self->pinmin = header[12]<<24|header[13]<<16|header[14]<<8|header[15];
  185.     self->pinmax = header[16]<<24|header[17]<<16|header[18]<<8|header[19];
  186.     */
  187.     if (header[20]) {
  188.         PyObject *obj;
  189.         int rv;
  190.         header[99] = 0;    /* just to be sure... */
  191.         obj = PyString_FromString((char *)header+20);
  192.         rv = PyDict_SetItemString(self->dict, "name", obj);
  193.         Py_XDECREF(obj);
  194.     }
  195.     self->colormapID = header[104]<<24|header[105]<<16|
  196.                header[106]<<8|header[107];
  197.     if (self->colormapID == 1) {
  198.         if (self->nchannels != 1)
  199.             goto BadRGB;
  200.         PyDict_SetItemString(self->dict, "format", format_rgb8);
  201.         PyDict_SetItemString(self->dict, "format_choices",
  202.                      format_rgb8_choices);
  203.         return 1;
  204.     } else if (self->colormapID != 0)
  205.         goto BadRGB;
  206.     if (self->nchannels == 1) {
  207.         PyDict_SetItemString(self->dict, "format", format_grey);
  208.         PyDict_SetItemString(self->dict, "format_choices",
  209.                      format_grey_choices);
  210.     } else {
  211.         PyDict_SetItemString(self->dict, "format", format_rgb);
  212.         PyDict_SetItemString(self->dict, "format_choices",
  213.                      format_rgb_choices);
  214.     }
  215.     return 1;
  216.  
  217.   BadRGB:
  218.     PyErr_SetString(errobject, "Illegal or unsupported RGB file");
  219.     return 0;
  220. }
  221.  
  222. static int
  223. initrgbreader(self, name)
  224.     rgbobject *self;
  225.     char *name;
  226. {
  227.     char *name_copy;
  228.  
  229.     if ((name_copy = malloc(strlen(name)+1)) == NULL) {
  230.         PyErr_NoMemory();
  231.         return 0;
  232.     }
  233.     strcpy(name_copy, name);
  234.     self->filename = name_copy;
  235.     self->is_reader = 1;
  236.     self->filep = fopen(name, "rb");
  237.     if (self->filep == NULL) {
  238.         PyErr_SetFromErrno(PyExc_IOError);
  239.         return 0;
  240.     }
  241.     if (!RGBreadheader(self))
  242.         return 0;
  243.     if (PyErr_Occurred())
  244.         return 0;
  245.     return 1;
  246. }
  247.  
  248. static int
  249. initrgbwriter(self, name)
  250.     rgbobject *self;
  251.     char *name;
  252. {
  253.     char *name_copy;
  254.  
  255.     if ((name_copy = malloc(strlen(name)+1)) == NULL) {
  256.         PyErr_NoMemory();
  257.         return 0;
  258.     }
  259.     strcpy(name_copy, name);
  260.     self->filename = name_copy;
  261.     self->is_reader = 0;
  262.     PyDict_SetItemString(self->dict, "format", format_rgb);
  263.     PyDict_SetItemString(self->dict, "format_choices", format_choices);
  264.     if (PyErr_Occurred())
  265.         return 0;
  266.     return 1;
  267. }
  268.  
  269. /* rgb methods */
  270.  
  271. static void
  272. rgb_dealloc(xp)
  273.     rgbobject *xp;
  274. {
  275.     Py_XDECREF(xp->dict);
  276.     if (xp->filename)
  277.         free(xp->filename);
  278.     if (xp->filep)
  279.         fclose(xp->filep);
  280.     PyMem_DEL(xp);
  281. }
  282.  
  283. static PyObject *
  284. RGBreadimage(self, fmt)
  285.     rgbobject *self;
  286.     PyObject *fmt;
  287. {
  288.     int x, y, z;
  289.     int v;
  290.     PyObject *rv;
  291.     char *ptr;
  292.     int xstride, ystride, zstride;
  293.  
  294.     x = self->width;
  295.     y = self->height;
  296.     z = self->nchannels;
  297.     if (z == 1) {
  298.         if (is_alignfmt(fmt))
  299.             x = (x + 3) & ~3;
  300.         rv = PyString_FromStringAndSize(NULL, x * y);
  301.         if (rv == NULL)
  302.             return NULL;
  303.         ptr = PyString_AS_STRING((PyStringObject *) rv);
  304.         memset(ptr, 0, x * y);
  305.         if (is_b2tfmt(fmt)) {
  306.             ystride = x;
  307.         } else {
  308.             ptr += x * (y - 1);
  309.             ystride = -x;
  310.         }
  311.         ystride -= self->width;
  312.         zstride = 0;
  313.         xstride = 1;
  314.     } else {
  315.         rv = PyString_FromStringAndSize(NULL, x * y * 4);
  316.         if (rv == NULL)
  317.             return NULL;
  318.         ptr = PyString_AS_STRING((PyStringObject *) rv);
  319.         memset(ptr, 0, x * y * 4);
  320.         if (is_b2tfmt(fmt)) {
  321.             ystride = x * 4;
  322.         } else {
  323.             ptr += x * (y - 1) * 4;
  324.             ystride = -x * 4;
  325.         }
  326.         ptr += 3;
  327.         zstride = -ystride * y - 1;
  328.         ystride -= x * 4;
  329.         xstride = 4;
  330.     }
  331.     for (z = self->nchannels; z > 0; z--) {
  332.         for (y = self->height; y > 0; y--) {
  333.             for (x = self->width; x > 0; x--) {
  334.                 v = getc(self->filep);
  335.                 if (v == EOF) {
  336.                     PyErr_SetString(errobject,
  337.                             "Truncated RGB file");
  338.                     Py_DECREF(rv);
  339.                     return NULL;
  340.                 }
  341.                 *ptr = v;
  342.                 ptr += xstride;
  343.             }
  344.             ptr += ystride;
  345.         }
  346.         ptr += zstride;
  347.     }
  348.     return rv;
  349. }
  350.  
  351. static int
  352. readtab(filep, tab, len)
  353.     FILE *filep;
  354.     unsigned long *tab;
  355.     int len;
  356. {
  357.     int ch;
  358.     unsigned long v;
  359.     int i;
  360.  
  361.     while (--len >= 0) {
  362.         v = 0;
  363.         for (i = 4; i > 0; i--) {
  364.             if ((ch = getc(filep)) == EOF) {
  365.                 PyErr_SetString(errobject,
  366.                         "Truncated RGB file");
  367.                 return 0;
  368.             }
  369.             v = v << 8 | ch;
  370.         }
  371.         *tab++ = v;
  372.     }
  373.     return 1;
  374. }
  375.  
  376. static int
  377. writetab(filep, tab, len)
  378.     FILE *filep;
  379.     unsigned long *tab;
  380.     int len;
  381. {
  382.     int ch;
  383.     unsigned long v;
  384.     int i;
  385.  
  386.     while (--len >= 0) {
  387.         v = *tab++;
  388.         for (i = 4; i > 0; i--) {
  389.             putc(v >> 24, filep);
  390.             v <<= 8;
  391.         }
  392.     }
  393.     if (ferror(filep))
  394.         return 0;
  395.     return 1;
  396. }
  397.  
  398. static int
  399. compare(p1, p2)
  400.     const void *p1, *p2;
  401. {
  402.     return ** (const unsigned long **) p1 - ** (const unsigned long **) p2;
  403. }
  404.  
  405. static PyObject *
  406. RGBreadrleimage(self, fmt)
  407.     rgbobject *self;
  408.     PyObject *fmt;
  409. {
  410.     int tablen = self->height * self->nchannels;
  411.     unsigned long *starttab = malloc(tablen * sizeof(unsigned long));
  412.     unsigned long *lengthtab = malloc(tablen * sizeof(unsigned long));
  413.     unsigned long **sorttab = malloc(tablen * sizeof(unsigned long *));
  414.     unsigned long **sorttab_ptr;
  415.     unsigned long seek_ptr, seek_pos;
  416.     int i, prev_row;
  417.     int width, n, y, z, stride;
  418.     PyObject *rv;
  419.     unsigned char *ptr, *optr, *cptr;
  420.     int reverse;
  421.  
  422.     if (starttab == NULL || lengthtab == NULL || sorttab == NULL) {
  423.         PyErr_NoMemory();
  424.         goto ErrorExit;
  425.     }
  426.     if (!readtab(self->filep, starttab, tablen))
  427.         goto ErrorExit;
  428.     if (!readtab(self->filep, lengthtab, tablen))
  429.         goto ErrorExit;
  430.     for (i = 0; i < tablen; i++)
  431.         sorttab[i] = &starttab[i];
  432.     qsort(sorttab, tablen, sizeof(unsigned long *), compare);
  433.     sorttab_ptr = sorttab;
  434.  
  435.     width = self->width;
  436.     if (self->nchannels == 1) {
  437.         if (is_alignfmt(fmt))
  438.             width = (width + 3) & ~3;
  439.         stride = 1;
  440.     } else {
  441.         stride = 4;
  442.         width *= 4;
  443.     }
  444.     if (!is_b2tfmt(fmt)) {
  445.         /* If height > 1, the following is still != 0, and it
  446.                    is very handy to have this number floating around
  447.                    when we have to reverse the image.  If height == 0
  448.                    or 1 it doesn't matter whether we reverse or not */
  449.         reverse = width * (self->height - 1);
  450.     } else
  451.         reverse = 0;
  452.     rv = PyString_FromStringAndSize(NULL, width * self->height);
  453.     if (rv == NULL)
  454.         goto ErrorExit;
  455.     ptr = (unsigned char *)PyString_AS_STRING((PyStringObject *) rv);
  456.     memset(ptr, 0, width * self->height);
  457.  
  458.     prev_row = -1;
  459.     seek_pos = 512 + 2 * tablen * 4;
  460.     while (--tablen >= 0) {
  461.         i = *sorttab_ptr++ - starttab;
  462.         seek_ptr = starttab[i];
  463.         if (seek_ptr == prev_row) {
  464.             /* copy a row */
  465.             if (reverse) {
  466.                 cptr = ptr + reverse - y * width;
  467.                 optr = ptr + reverse - (i%self->height)*width;
  468.             } else {
  469.                 cptr = ptr + y * width;
  470.                 optr = ptr + (i % self->height) * width;
  471.             }
  472.             cptr += stride - z - 1;
  473.             optr += stride - (i / self->height) - 1;
  474.             n = self->width;
  475.             while (--n >= 0) {
  476.                 *optr = *cptr;
  477.                 optr += stride;
  478.                 cptr += stride;
  479.             }
  480.             continue;
  481.         }
  482.         if (seek_pos != seek_ptr) {
  483.             while (seek_pos < seek_ptr) {
  484.                 if ((n = getc(self->filep)) == EOF)
  485.                     goto EOFError;
  486.                 seek_pos++;
  487.             }
  488.             if (seek_pos > seek_ptr) {
  489.                 PyErr_SetString(errobject, "Bad rle table");
  490.                 goto ErrorExit;
  491.             }
  492.         }
  493.         z = i / self->height;
  494.         y = i % self->height;
  495.         if (reverse)
  496.             optr = ptr + reverse - y * width;
  497.         else
  498.             optr = ptr + y * width;
  499.         optr += stride - z - 1;
  500.         for (;;) {
  501.             int pixel, count;
  502.             if ((pixel = getc(self->filep)) == EOF)
  503.                 goto EOFError;
  504.             seek_pos++;
  505.             if (!(count = (pixel & 0x7f)))
  506.                 break;
  507.             if (pixel & 0x80) {
  508.                 while (count--) {
  509.                     if ((pixel = getc(self->filep)) == EOF)
  510.                         goto EOFError;
  511.                     seek_pos++;
  512.                     *optr = pixel;
  513.                     optr += stride;
  514.                 }
  515.             } else {
  516.                 if ((pixel = getc(self->filep)) == EOF)
  517.                     goto EOFError;
  518.                 seek_pos++;
  519.                 while (count--) {
  520.                     *optr = pixel;
  521.                     optr += stride;
  522.                 }
  523.             }
  524.         }
  525.         prev_row = seek_ptr;
  526.     }
  527.  
  528.     free(starttab);
  529.     free(lengthtab);
  530.     free(sorttab);
  531.     return rv;
  532.  
  533.   EOFError:
  534.     PyErr_SetString(errobject, "Truncated RGB file");
  535.   ErrorExit:
  536.     if (starttab)
  537.         free(starttab);
  538.     if (lengthtab)
  539.         free(lengthtab);
  540.     if (sorttab)
  541.         free(sorttab);
  542.     return NULL;
  543. }
  544.  
  545. static char doc_read[] = "Read the actual image data as a string.";
  546.  
  547. static PyObject *
  548. rgb_read(self, args)
  549.     rgbobject *self;
  550.     PyObject *args;
  551. {
  552.         PyObject *fmt;
  553.     
  554.     if (!PyArg_ParseTuple(args,""))
  555.         return NULL;
  556.     if (!self->is_reader) {
  557.         PyErr_SetString(errobject, "Cannot read() from writer object");
  558.         return NULL;
  559.     }
  560.     /* Get format (and other args), check, read data, return it */
  561.     if (!rgbselfattr(self, "format", "O", &fmt, 1))
  562.         return NULL;
  563.     if (self->nchannels == 1) {
  564.         if (self->colormapID == 0) {
  565.             if (!is_greyfmt(fmt))
  566.                 goto illegal_format;
  567.         } else {
  568.             /* self->colormapID == 1 */
  569.             if (!is_ditherfmt(fmt))
  570.                 goto illegal_format;
  571.         }
  572.     } else {
  573.         if (!is_4bytefmt(fmt))
  574.             goto illegal_format;
  575.     }
  576.     if (self->rle)
  577.         return RGBreadrleimage(self, fmt);
  578.     else
  579.         return RGBreadimage(self, fmt);
  580.  
  581.   illegal_format:
  582.     PyErr_SetString(errobject, "Illegal format");
  583.     return NULL;
  584. }
  585.  
  586. #define putbyte(p,x)    ((p)[0]=(x))
  587. #define putshort(p,x)    ((p)[0]=(x)>>8,(p)[1]=(x))
  588. #define putlong(p,x) ((p)[0]=(x)>>24,(p)[1]=(x)>>16,(p)[2]=(x)>>8,(p)[3]=(x))
  589.  
  590. static int
  591. RGBwriteheader(self, fmt)
  592.     rgbobject *self;
  593.     PyObject *fmt;
  594. {
  595.     char *name;
  596.     char header[512];
  597.  
  598.     memset(header, 0, 512);
  599.     putshort(header+0, 474);
  600.     putbyte(header+2, self->rle);
  601.     putbyte(header+3, 1);
  602.     if (self->nchannels == 1)
  603.         if (self->height == 1)
  604.             putshort(header+4, 1);
  605.         else
  606.             putshort(header+4, 2);
  607.     else
  608.         putshort(header+4, 3);
  609.     putshort(header+6, self->width);
  610.     putshort(header+8, self->height);
  611.     putshort(header+10, self->nchannels);
  612.     putlong(header+12, 0);
  613.     putlong(header+16, 255);
  614.     if (!rgbselfattr(self, "name", "s", &name, 0))
  615.         name = "no name";
  616.     strncpy(header+24, name, 79);
  617.     if (is_ditherfmt(fmt))
  618.         putlong(header+104, 1);
  619.     else
  620.         putlong(header+104, 0);
  621.     if (fwrite(header, 1, 512, self->filep) != 512) {
  622.         PyErr_SetFromErrno(PyExc_IOError);
  623.         return 0;
  624.     }
  625.     return 1;
  626. }
  627.  
  628. static PyObject *
  629. RGBwriteimage(self, fmt, ptr)
  630.     rgbobject *self;
  631.     PyObject *fmt;
  632.     unsigned char *ptr;
  633. {
  634.     int x, y, z;
  635.     int v;
  636.     int xstride, ystride, zstride;
  637.  
  638.     x = self->width;
  639.     y = self->height;
  640.     z = self->nchannels;
  641.     if (z == 1) {
  642.         if (is_alignfmt(fmt))
  643.             x = (x + 3) & ~3;
  644.         if (is_b2tfmt(fmt)) {
  645.             ystride = x;
  646.         } else {
  647.             ptr += x * (y - 1);
  648.             ystride = -x;
  649.         }
  650.         ystride -= self->width;
  651.         zstride = 0;
  652.         xstride = 1;
  653.     } else {
  654.         if (is_b2tfmt(fmt)) {
  655.             ystride = x * 4;
  656.         } else {
  657.             ptr += x * (y - 1) * 4;
  658.             ystride = -x * 4;
  659.         }
  660.         ptr += 3;
  661.         zstride = -ystride * y - 1;
  662.         ystride -= x * 4;
  663.         xstride = 4;
  664.     }
  665.     for (z = self->nchannels; z > 0; z--) {
  666.         for (y = self->height; y > 0; y--) {
  667.             for (x = self->width; x > 0; x--) {
  668.                 putc(*ptr, self->filep);
  669.                 ptr += xstride;
  670.             }
  671.             ptr += ystride;
  672.         }
  673.         ptr += zstride;
  674.     }
  675.  
  676.     fflush(self->filep);
  677.  
  678.     Py_INCREF(Py_None);
  679.     return Py_None;
  680. }
  681.  
  682. static int
  683. compressrow(iptr, rlebuf, length, stride)
  684.     unsigned char *iptr;
  685.     unsigned char *rlebuf;
  686.     int length, stride;
  687. {
  688.     unsigned char *ibufend = iptr + length * stride;
  689.     unsigned char *optr = rlebuf;
  690.     unsigned char *sptr;
  691.     int stride2 = 2 * stride;
  692.     int count, todo, cc;
  693.  
  694.     while (iptr < ibufend) {
  695.         sptr = iptr;
  696.         iptr += stride2;
  697.         while (iptr < ibufend && (iptr[-stride2] != iptr[-stride] ||
  698.                       iptr[-stride] != iptr[0]))
  699.             iptr += stride;
  700.         iptr -= stride2;
  701.         count = (iptr - sptr) / stride;
  702.         while (count) {
  703.             todo = count > 126 ? 126 : count;
  704.             count -= todo;
  705.             *optr++ = 0x80 | todo;
  706.             while (--todo >= 0) {
  707.                 *optr++ = *sptr;
  708.                 sptr += stride;
  709.             }
  710.         }
  711.         sptr = iptr;
  712.         cc = *iptr;
  713.         iptr += stride;
  714.         while (iptr < ibufend && *iptr == cc)
  715.             iptr += stride;
  716.         count = (iptr - sptr) / stride;
  717.         while (count) {
  718.             todo = count > 126 ? 126 : count;
  719.             count -= todo;
  720.             *optr++ = todo;
  721.             *optr++ = cc;
  722.         }
  723.     }
  724.     *optr++ = 0;
  725.     return optr - rlebuf;
  726. }
  727.  
  728. static PyObject *
  729. RGBwriterleimage(self, fmt, ptr)
  730.     rgbobject *self;
  731.     PyObject *fmt;
  732.     unsigned char *ptr;
  733. {
  734.     unsigned char *rlebuf;
  735.     unsigned long *starttab, *lengthtab;
  736.     long pos;
  737.     int tablen, rlebuflen, len;
  738.     int i, y, z;
  739.     int xsize, ysize, zsize, xstride, ystride;
  740.  
  741.     xsize = ystride = self->width;
  742.     ysize = self->height;
  743.     zsize = self->nchannels;
  744.     tablen = ysize * zsize;
  745.     rlebuflen = 1.05 * xsize + 10;
  746.     starttab = (unsigned long *) malloc(tablen * sizeof(unsigned long));
  747.     lengthtab = (unsigned long *) malloc(tablen * sizeof(unsigned long));
  748.     rlebuf = (unsigned char *) malloc(rlebuflen);
  749.  
  750.     if (starttab == NULL || lengthtab == NULL || rlebuf == NULL) {
  751.         PyErr_NoMemory();
  752.         goto ErrorExit;
  753.     }
  754.     if (zsize == 1) {
  755.         if (is_alignfmt(fmt))
  756.             ystride = (ystride + 3) & ~3;
  757.         xstride = 1;
  758.     } else {
  759.         xstride = 4;
  760.         ystride *= 4;
  761.         ptr += 3;
  762.     }
  763.     if (!is_b2tfmt(fmt)) {
  764.         ptr += ystride * (ysize - 1);
  765.         ystride = -ystride;
  766.     }
  767.     ystride += zsize;
  768.  
  769.     pos = 512 + 2 * tablen * 4;
  770.     if (fseek(self->filep, pos, SEEK_SET))
  771.         goto WriteError;
  772.     i = 0;
  773.     for (y = ysize; y > 0; y--) {
  774.         for (z = zsize; z > 0; z--) {
  775.             len = compressrow(ptr, rlebuf, xsize, xstride);
  776.             if (len > rlebuflen) {
  777.                 PyErr_SetString(errobject,
  778.                     "rlebuf is too small - bad poop");
  779.                 goto ErrorExit;
  780.             }
  781.             starttab[i] = pos;
  782.             lengthtab[i] = len;
  783.             if (fwrite(rlebuf, 1, len, self->filep) != len)
  784.                 goto WriteError;
  785.             pos += len;
  786.             ptr--;
  787.             i += ysize;
  788.         }
  789.         ptr += ystride;
  790.         i += 1 - tablen;
  791.     }
  792.  
  793.     if (fseek(self->filep, 512, SEEK_SET))
  794.         goto WriteError;
  795.     if (!writetab(self->filep, starttab, tablen))
  796.         goto WriteError;
  797.     if (!writetab(self->filep, lengthtab, tablen))
  798.         goto WriteError;
  799.     fseek(self->filep, 0, SEEK_END);
  800.     fflush(self->filep);
  801.  
  802.     free(starttab);
  803.     free(lengthtab);
  804.     free(rlebuf);
  805.  
  806.     Py_INCREF(Py_None);
  807.     return Py_None;
  808.  
  809.   WriteError:
  810.     PyErr_SetString(errobject, "Error writing RGB file");
  811.  
  812.   ErrorExit:
  813.     if (starttab)
  814.         free(starttab);
  815.     if (lengthtab)
  816.         free(lengthtab);
  817.     if (rlebuf)
  818.         free(rlebuf);
  819.     return NULL;
  820. }
  821.  
  822.  
  823. static char doc_write[] = "Write the image data.";
  824.  
  825. static PyObject *
  826. rgb_write(self, args)
  827.     rgbobject *self;
  828.     PyObject *args;
  829. {
  830.         unsigned char *data;
  831.     int datalen;
  832.     int i, width;
  833.     PyObject *fmt;
  834.     
  835.     if (!PyArg_ParseTuple(args, "s#", &data, &datalen))
  836.         return NULL;
  837.     if (self->is_reader) {
  838.         PyErr_SetString(errobject, "Cannot write() to reader object");
  839.         return NULL;
  840.     }
  841.     if (!rgbselfattr(self, "width", "i", &self->width, 1) ||
  842.         !rgbselfattr(self, "height", "i", &self->height, 1) ||
  843.         !rgbselfattr(self, "format", "O", &fmt, 1))
  844.         return NULL;
  845.     if (is_4bytefmt(fmt)) {
  846.         if (!rgbselfattr(self, "rgba", "i", &i, 0))
  847.             i = 0;
  848.         if (i)
  849.             self->nchannels = 4;
  850.         else
  851.             self->nchannels = 3;
  852.         self->colormapID = 0;
  853.         i = 4;
  854.     } else {
  855.         self->nchannels = 1;
  856.         if (is_ditherfmt(fmt))
  857.             self->colormapID = 1;
  858.         else
  859.             self->colormapID = 0;
  860.         i = 1;
  861.     }
  862.     width = self->width;
  863.     if (is_alignfmt(fmt))
  864.         width = (width + 3) & ~3;
  865.     if (width * self->height * i != datalen) {
  866.         PyErr_SetString(errobject, "Incorrect datasize");
  867.         return NULL;
  868.     }
  869.     self->filep = fopen(self->filename, "wb");
  870.     if (self->filep == NULL) {
  871.         PyErr_SetFromErrno(PyExc_IOError);
  872.         return NULL;
  873.     }
  874.  
  875.     if (!rgbselfattr(self, "rle", "i", &self->rle, 0))
  876.         self->rle = 1;
  877.  
  878.     if (!RGBwriteheader(self, fmt))
  879.         return NULL;
  880.  
  881.     if (self->rle)
  882.         return RGBwriterleimage(self, fmt, data);
  883.     else
  884.         return RGBwriteimage(self, fmt, data);
  885. }
  886.  
  887. static struct PyMethodDef rgb_methods[] = {
  888.     {"read",    (PyCFunction)rgb_read,    1,    doc_read},
  889.     {"write",    (PyCFunction)rgb_write,    1,    doc_write},
  890.     {NULL,        NULL}        /* sentinel */
  891. };
  892.  
  893. static PyObject *
  894. rgb_getattr(xp, name)
  895.     rgbobject *xp;
  896.     char *name;
  897. {
  898.         PyObject *v;
  899.     
  900.     if (xp->dict != NULL) {
  901.             if (strcmp(name, "__dict__") == 0) {
  902.                 Py_INCREF(xp->dict);
  903.             return xp->dict;
  904.         }
  905.         if (strcmp(name, "__doc__") == 0) {
  906.                 return PyString_FromString(doc_rgb);
  907.         }
  908.         v = PyDict_GetItemString(xp->dict, name);
  909.         if (v != NULL) {
  910.             Py_INCREF(v);
  911.             return v;
  912.         }
  913.     }
  914.     return Py_FindMethod(rgb_methods, (PyObject *)xp, name);
  915. }
  916.  
  917. static int
  918. rgb_setattr(xp, name, v)
  919.     rgbobject *xp;
  920.     char *name;
  921.     PyObject *v;
  922. {
  923.     if (xp->dict == NULL) {
  924.         xp->dict = PyDict_New();
  925.         if (xp->dict == NULL)
  926.             return -1;
  927.     }
  928.     if (v == NULL) {
  929.         int rv = PyDict_DelItemString(xp->dict, name);
  930.         if (rv < 0)
  931.             PyErr_SetString(PyExc_AttributeError,
  932.                     "delete non-existing imgrgb attribute");
  933.         return rv;
  934.     }
  935.     else
  936.         return PyDict_SetItemString(xp->dict, name, v);
  937. }
  938.  
  939. static PyTypeObject Rgbtype = {
  940.     PyObject_HEAD_INIT(&PyType_Type)
  941.     0,            /*ob_size*/
  942.     "imgrgb",        /*tp_name*/
  943.     sizeof(rgbobject),    /*tp_basicsize*/
  944.     0,            /*tp_itemsize*/
  945.     /* methods */
  946.     (destructor)rgb_dealloc, /*tp_dealloc*/
  947.     0,            /*tp_print*/
  948.     (getattrfunc)rgb_getattr, /*tp_getattr*/
  949.     (setattrfunc)rgb_setattr, /*tp_setattr*/
  950.     0,            /*tp_compare*/
  951.     0,            /*tp_repr*/
  952.     0,            /*tp_as_number*/
  953.     0,            /*tp_as_sequence*/
  954.     0,            /*tp_as_mapping*/
  955.     0,            /*tp_hash*/
  956. };
  957.  
  958. static char doc_newreader[] = "Create reader for file passed as arg.";
  959.  
  960. static PyObject *
  961. rgb_newreader(self, args)
  962.     PyObject *self;
  963.     PyObject *args;
  964. {
  965.         char *filename;
  966.     rgbobject *obj;
  967.     
  968.     if (!PyArg_ParseTuple(args, "s", &filename))
  969.         return NULL;
  970.     if ((obj = newrgbobject()) == NULL)
  971.         return NULL;
  972.     if (!initrgbreader(obj, filename)) {
  973.         rgb_dealloc(obj);
  974.         return NULL;
  975.     }
  976.     return (PyObject *)obj;
  977. }
  978.  
  979. static char doc_newwriter[] = "Create writer for file passed as arg.";
  980.  
  981. static PyObject *
  982. rgb_newwriter(self, args)
  983.     PyObject *self;
  984.     PyObject *args;
  985. {
  986.         char *filename;
  987.     rgbobject *obj;
  988.     
  989.     if (!PyArg_ParseTuple(args, "s", &filename))
  990.         return NULL;
  991.     if ((obj = newrgbobject()) == NULL)
  992.         return NULL;
  993.     if (!initrgbwriter(obj, filename)) {
  994.         rgb_dealloc(obj);
  995.         return NULL;
  996.     }
  997.     return (PyObject *)obj;
  998. }
  999.  
  1000.  
  1001. /* List of functions defined in the module */
  1002.  
  1003. static struct PyMethodDef rgb_module_methods[] = {
  1004.     {"reader",    rgb_newreader,    1,    doc_newreader},
  1005.     {"writer",    rgb_newwriter,    1,    doc_newwriter},
  1006.     {NULL,        NULL}        /* sentinel */
  1007. };
  1008.  
  1009.  
  1010. /* Initialization function for the module (*must* be called initimgrgb) */
  1011. static char doc_imgrgb[] = "Module that reads and writes RGB image files.";
  1012.  
  1013. void
  1014. initimgsgi()
  1015. {
  1016.     PyObject *m, *d, *x, *formatmodule, *formatdict;
  1017.  
  1018.     /* Create the module and add the functions */
  1019.     m = Py_InitModule("imgsgi", rgb_module_methods);
  1020.  
  1021.     /* Add some symbolic constants to the module */
  1022.     d = PyModule_GetDict(m);
  1023.     errobject = PyString_FromString("imgsgi.error");
  1024.     PyDict_SetItemString(d, "error", errobject);
  1025.     x = PyString_FromString(doc_imgrgb);
  1026.     PyDict_SetItemString(d, "__doc__", x);
  1027.  
  1028.     /* Get supported formats */
  1029.     if ((formatmodule = PyImport_ImportModule("imgformat")) == NULL)
  1030.         Py_FatalError("imgsgi depends on imgformat");
  1031.     if ((formatdict = PyModule_GetDict(formatmodule)) == NULL)
  1032.         Py_FatalError("imgformat has no dict");
  1033.  
  1034.     format_rgb = PyDict_GetItemString(formatdict,"rgb");
  1035.     format_rgb_b2t = PyDict_GetItemString(formatdict,"rgb_b2t");
  1036.     format_rgb_choices = Py_BuildValue("(OO)", format_rgb, format_rgb_b2t);
  1037.  
  1038.     format_grey = PyDict_GetItemString(formatdict,"grey");
  1039.     format_grey_b2t = PyDict_GetItemString(formatdict,"grey_b2t");
  1040.     format_xgrey = PyDict_GetItemString(formatdict,"xgrey");
  1041.     format_xgrey_b2t = PyDict_GetItemString(formatdict,"xgrey_b2t");
  1042.     format_grey_choices = Py_BuildValue("(OOOO)",
  1043.                         format_grey, format_grey_b2t,
  1044.                         format_xgrey, format_xgrey_b2t);
  1045.  
  1046.     format_rgb8 = PyDict_GetItemString(formatdict,"rgb8");
  1047.     format_rgb8_b2t = PyDict_GetItemString(formatdict,"rgb8_b2t");
  1048.     format_xrgb8 = PyDict_GetItemString(formatdict,"xrgb8");
  1049.     format_xrgb8_b2t = PyDict_GetItemString(formatdict,"xrgb8_b2t");
  1050.     format_rgb8_choices = Py_BuildValue("(OOOO)",
  1051.                         format_rgb8, format_rgb8_b2t,
  1052.                         format_xrgb8, format_xrgb8_b2t);
  1053.  
  1054.     format_choices = Py_BuildValue("(OOOOOOOOOO)",
  1055.                        format_rgb, format_rgb_b2t,
  1056.                        format_grey, format_grey_b2t,
  1057.                        format_xgrey, format_xgrey_b2t,
  1058.                        format_rgb8, format_rgb8_b2t,
  1059.                        format_xrgb8, format_xrgb8_b2t);
  1060.  
  1061.     /* Check for errors */
  1062.     if (PyErr_Occurred())
  1063.         Py_FatalError("can't initialize module imgsgi");
  1064. }
  1065.